home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 176-200 / disk_191 / blk / lex.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  17KB  |  897 lines

  1. /*
  2.  * Lexical Analysis Functions Takes input stream and returns "tokens".
  3.  * Implements pre-processor, allowing #include's and #define's tokens are
  4.  * character strings.
  5.  */
  6. #include <stdio.h>
  7. #include <ctype.h>
  8. #include <functions.h>
  9. #include <exec/types.h>
  10. #include "listmac.h"
  11. #include "std.h"
  12. #include "lex.h"
  13.  
  14.  
  15. struct StringElt {
  16.     struct Nod      node;
  17.     char           *buf;
  18. };
  19.  
  20. struct Token {
  21.     struct Nod      node;
  22.     char           *buf;
  23.     short           type, can_be_macro;
  24.     char            chr;
  25. };
  26.  
  27. struct Macro {
  28.     struct Nod      node;
  29.     struct Lst      arguments;
  30.     struct Lst      tokens;
  31.     char           *name;
  32.     short           active, nargs;
  33. };
  34.  
  35. struct ActiveMacro {
  36.     struct Nod      node;
  37.     struct Macro   *mac;
  38.     struct Token   *curtok;
  39. };
  40.  
  41. struct LexFile {
  42.     struct Nod      node;
  43.     FILE           *fp;
  44.     short           newln;
  45.     short           nextchar;
  46. };
  47.  
  48. struct IfBlock {
  49.     struct Nod     *node;
  50.     short           skip_else;
  51. };
  52.  
  53.  
  54. struct Token   *CopyToken ();
  55. struct Macro   *FindMacro ();
  56. char           *FindString ();
  57.  
  58.  
  59. #define BUFLEN 100
  60. static char     buf[BUFLEN];
  61. static short    bufpos;
  62. static struct Token
  63.         rawtok, *lasttok, *retok;
  64.  
  65. static struct LexFile *toplf;
  66. static struct Lst macros, activemacs, files, ifblocks;
  67. static short    files_open = 0, init_done = 0;
  68. static char    *includestr, *definestr, *ifdefstr,
  69.         *ifndefstr, *elsestr, *endifstr;
  70.  
  71. static char    *libbase = "hd:include/", libnam[80], basnam[80];
  72.  
  73. #define NHASH 53
  74. struct Lst      hashTab[NHASH];
  75.  
  76.  
  77. /*
  78.  * Open a file for lexing - could be an include.
  79.  * Set newline flag to true.
  80.  */
  81. short OpenLexFile (name)
  82.     char           *name;
  83. {
  84.     struct LexFile *lf;
  85.     struct Macro   *mac;
  86.  
  87.     /*
  88.      * on first call, init environment vars 
  89.      */
  90.     if (!init_done) {
  91.         NewList (&files);
  92.         NewList (¯os);
  93.         NewList (&ifblocks);
  94.         NewList (&activemacs);
  95.         InitHash ();
  96.         includestr = FindString ("include");
  97.         definestr = FindString ("define");
  98.         ifdefstr = FindString ("ifdef");
  99.         ifndefstr = FindString ("ifndef");
  100.         elsestr = FindString ("else");
  101.         endifstr = FindString ("endif");
  102.         init_done = 1;
  103.     }
  104.  
  105.     /*
  106.      * if there are no files currently open, prepare for a new one 
  107.      */
  108.     if (!files_open) {
  109.         while (mac = (struct Macro *) RemHead (¯os))
  110.             FreeMacro (mac);
  111.         ASSERT (!TEST (HEAD (&ifblocks)));
  112.         ASSERT (!TEST (HEAD (&activemacs)));
  113.         retok = NULL;
  114.     }
  115.     lf = NEW (struct LexFile);
  116.     if (!lf) return 0;
  117.  
  118.     /*
  119.      * printf ("opening \"%s\"\n", name); 
  120.      */
  121.     lf->fp = fopen (name, "r");
  122.     if (!lf->fp) {
  123.         FREI (lf);
  124.         return 0;
  125.     }
  126.     lf->newln = 1;
  127.     lf->nextchar = getc (lf->fp);
  128.     AddHead (&files, lf);
  129.     toplf = lf;
  130.  
  131.     if (feof (lf->fp)) {
  132.         CloseLexFile ();
  133.         return 0;
  134.     }
  135.     files_open = 1;
  136.     return 1;
  137. }
  138.  
  139.  
  140. /*
  141.  * Close top file returning 1 for the last file now closed.
  142.  */
  143. short CloseLexFile ()
  144. {
  145.     if (!files_open) return 1;
  146.  
  147.     fclose (toplf->fp);
  148.     Remove (toplf);
  149.     FREI (toplf);
  150.     toplf = HEAD (&files);
  151.     if (TEST (toplf)) return 0;
  152.     files_open = 0;
  153.     toplf = 0;
  154.     return 1;
  155. }
  156.  
  157.  
  158. /*
  159.  * free stuff up 
  160.  */
  161. LexCleanup ()
  162. {
  163.     struct Macro   *mac, *nmac;
  164.  
  165.     while (files_open) CloseLexFile ();
  166.  
  167.     FreeHash ();
  168.     for (mac = HEAD (¯os); nmac = NEXT (mac); mac = nmac)
  169.         FreeMacro (mac);
  170.     ASSERT (!TEST (HEAD (&activemacs)));
  171.  
  172.     /*
  173.      * initialization can be done again 
  174.      */
  175.     init_done = 0;
  176. }
  177.  
  178.  
  179. FreeMacro (mac)
  180.     struct Macro   *mac;
  181. {
  182.     struct Token   *tok;
  183.     struct StringElt *se;
  184.  
  185.     while (tok = (struct Token *) RemHead (&mac->tokens))
  186.         FREI (tok);
  187.     while (se = (struct StringElt *) RemHead (&mac->arguments))
  188.         FREI (se);
  189.     FREI (mac);
  190. }
  191.  
  192.  
  193. /*
  194.  * Get next raw character from file dealing with backslash escape chars.
  195.  * This is kind of wrong since these really ought to be recognized only
  196.  * inside strings.  Doing it this way deals with backslashes before
  197.  * newlines.
  198.  */
  199. short NextC1 (fp)
  200.     FILE           *fp;
  201. {
  202.     short           c, k, radix;
  203.  
  204.     c = getc (fp);
  205.     if (c == '\\') {
  206.         c = getc (fp);
  207.         if (isdigit (c)) {
  208.             radix = 10;
  209.             if (c == '0') radix = 8;
  210.             k = (c - '0');
  211.             while (isdigit (c = getc (fp)))
  212.                 k = k * radix + (c - '0');
  213.             ungetc (c, fp);
  214.             c = k;
  215.         } else {
  216.             switch (c) {
  217.                 case '\\':
  218.                 break;
  219.                 case '"':
  220.                 c = '.';
  221.                 break;
  222.                 case 'n':
  223.                 c = '\n';
  224.                 break;
  225.                 case 'b':
  226.                 c = '\b';
  227.                 break;
  228.                 case 't':
  229.                 c = '\t';
  230.                 break;
  231.                 case '\n':
  232.                 c = getc (fp);
  233.             }
  234.         }
  235.     }
  236.     return c;
  237. }
  238.  
  239.  
  240. /*
  241.  * Get next character from Top Lex File, updating nextchar.
  242.  */
  243. short NextC ()
  244. {
  245.     short           c;
  246.  
  247.     if (!toplf) return EOF;
  248.     c = toplf->nextchar;
  249.     toplf->nextchar = NextC1 (toplf->fp);
  250.     return c;
  251. }
  252.  
  253.  
  254. /*
  255.  * Move the top lex file to the next newline.
  256.  */
  257. AdvanceEOL ()
  258. {
  259.     if (!toplf) return;
  260.     while (toplf->nextchar != '\n' && !feof (toplf->fp))
  261.         NextC ();
  262. }
  263.  
  264.  
  265. /*
  266.  * Read a raw set of characters updating newline state.  Sets the
  267.  * fields in rawtok to the type and string pointer, if any. 
  268.  */
  269. RawToken ()
  270. {
  271.     short           c;
  272.     short           nochar = 1, comment, nl;
  273.  
  274.     bufpos = 0;
  275.     rawtok.can_be_macro = 1;
  276.  
  277.     /*
  278.      * get a non-blank char 
  279.      */
  280.     while (nochar) {
  281.  
  282.         /*
  283.          * get a character testing for end-of-file if EOF, just
  284.          * return that fact right away 
  285.          */
  286.         c = NextC ();
  287.         if (c == EOF) {
  288.             rawtok.type = RT_EOF;
  289.             return;
  290.         }
  291.  
  292.         /*
  293.          * test next char: if it's white space other than newline,
  294.          * just clear the newline flag and continue looking. 
  295.          */
  296.         if (isspace (c) && c != '\n')
  297.             toplf->newln = 0;
  298.         else {
  299.  
  300.             /*
  301.              * otherwise, this is potentially interesting, but it
  302.              * might be the start of a comment - if so, scan til
  303.              * the end of the comment (no nested comments ala
  304.              * "C").  If not, exit the loop. 
  305.              */
  306.             if (c == '/' && toplf->nextchar == '*') {
  307.                 comment = 1;
  308.                 while (comment) {
  309.                     c = NextC ();
  310.                     if (c == '*'
  311.                       && toplf->nextchar == '/') {
  312.                         c = NextC ();
  313.                         comment = 0;
  314.                     }
  315.                 }
  316.  
  317.             } else nochar = 0;
  318.         }
  319.     }
  320.  
  321.     nl = toplf->newln;
  322.     toplf->newln = 0;
  323.     bufpos = 0;
  324.  
  325.     /*
  326.      * otherwise, read out the cases id symbol starting with char 
  327.      */
  328.     if (nl && c == '#')
  329.         rawtok.type = RT_ESC;
  330.  
  331.     else if (c == '\'') {
  332.         rawtok.chr = NextC ();
  333.         c = NextC ();
  334.  
  335.         /*
  336.          * Skip past weird character constants, like 'FOOL',
  337.          * making no attempt to deal with them correctly.
  338.          */
  339.         comment = 0;
  340.         while (c != '\'' && comment < 6) {
  341.             c = NextC ();
  342.             comment++;
  343.         }
  344.         if (comment >= 6) printf ("error in character constant\n");
  345.         rawtok.type = RT_CHRC;
  346.  
  347.     } else if (isalpha (c) || c == '_') {
  348.         buf[bufpos++] = c;
  349.         while (isalnum (toplf->nextchar) || toplf->nextchar == '_')
  350.             buf[bufpos++] = NextC ();
  351.         buf[bufpos] = 0;
  352.         rawtok.type = RT_ID;
  353.         rawtok.buf = FindString (buf);
  354.  
  355.     } else if (c == '\n') {
  356.         toplf->newln = 1;
  357.         rawtok.type = RT_NEWLN;
  358.  
  359.     } else if (isdigit (c)) {
  360.         buf[bufpos++] = c;
  361.         while (isdigit (toplf->nextchar)) buf[bufpos++] = NextC ();
  362.         buf[bufpos] = 0;
  363.         rawtok.buf = FindString (buf);
  364.         rawtok.type = RT_NUM;
  365.  
  366.     } else if (c == '"') {
  367.         while ((c = NextC ()) != '"')
  368.             if (bufpos < BUFLEN)
  369.                 buf[bufpos++] = c;
  370.  
  371.         if (bufpos + 1 == BUFLEN) printf ("string overflow\n");
  372.         buf[bufpos] = 0;
  373.         rawtok.buf = FindString (buf);
  374.         rawtok.type = RT_STR;
  375.  
  376.     } else {
  377.         rawtok.chr = c;
  378.         rawtok.type = RT_CHR;
  379.     }
  380. }
  381.  
  382.  
  383. /*
  384.  * Main function -- returns string pointer for next token makes include files
  385.  * and macros transparent reads from top open file and returns pointer to
  386.  * string of chars could be a symbol, could be a number, could be a character 
  387.  */
  388. short NextToken (tbuf)
  389.     char          **tbuf;
  390. {
  391.     struct Token   *tok;
  392.     struct Macro   *mac;
  393.     struct ActiveMacro *amac;
  394.     short           i;
  395.     char            c;
  396.  
  397.     while () {
  398.  
  399.         /*
  400.          * get raw token from file or active macro 
  401.          */
  402.         amac = HEAD (&activemacs);
  403.         if (retok) {
  404.             tok = retok;
  405.             retok = NULL;
  406.         } else if (TEST (amac)) {
  407.             tok = amac->curtok;
  408.             if (!TEST (tok)) {
  409.                 amac->mac->active = 0;
  410.                 for (i = 0; i < amac->mac->nargs; i++)
  411.                     FreeMacro (RemHead (¯os));
  412.                 Remove (amac);
  413.                 FREI (amac);
  414.                 continue;
  415.             }
  416.             amac->curtok = NEXT (amac->curtok);
  417.         } else {
  418.             RawToken ();
  419.             tok = &rawtok;
  420.         }
  421.         switch (tok->type) {
  422.             case RT_EOF:
  423.             if (CloseLexFile ()) {
  424.                 lasttok = tok;
  425.                 return RT_EOF;
  426.             }
  427.             break;
  428.             case RT_ESC:
  429.             RawToken ();
  430.             if (rawtok.type != RT_ID) {
  431.                 printf ("real problem with lexer directive\n");
  432.                 AdvanceEOL ();
  433.                 break;
  434.             }
  435.             if (rawtok.buf == includestr)        Include ();
  436.             else if (rawtok.buf == definestr)    Define ();
  437.             else if (rawtok.buf == ifdefstr)    IfDef (1);
  438.             else if (rawtok.buf == ifndefstr)    IfDef (0);
  439.             else if (rawtok.buf == elsestr)        Else ();
  440.             else if (rawtok.buf == endifstr)    EndIf ();
  441.             else {
  442.                 printf ("unknown directive \"%s\"\n", rawtok.buf);
  443.                 AdvanceEOL ();
  444.             }
  445.             break;
  446.  
  447.             case RT_ID:
  448.  
  449.             /*
  450.              * test for known macros (if this token can be one) 
  451.              */
  452.             if (tok->can_be_macro) {
  453.                 mac = FindMacro (tok->buf);
  454.                 if (mac) {
  455.                     InstantiateMacro (mac);
  456.                     break;
  457.                 }
  458.             }
  459.             case RT_STR:
  460.             case RT_NUM:
  461.             *tbuf = tok->buf;
  462.             lasttok = tok;
  463.             return tok->type;
  464.             case RT_CHR:
  465.             case RT_CHRC:
  466.             *tbuf = &tok->chr;
  467.             lasttok = tok;
  468.             return tok->type;
  469.         }
  470.     }
  471. }
  472.  
  473.  
  474. /*
  475.  * The given macro has occured in the text, cause it to come into existence
  476.  * with argument substitution. 
  477.  */
  478. InstantiateMacro (mac)
  479.     struct Macro   *mac;
  480. {
  481.     struct ActiveMacro *amac;
  482.     struct StringElt *arg;
  483.     struct Macro   *smac;
  484.     struct Token   *tok;
  485.     char           *buf;
  486.     short           vtok, level, endarglist;
  487.  
  488.     if (mac->active) {
  489.         printf ("recursive macro ignored\n");
  490.         return;
  491.     }
  492.  
  493.     /*
  494.      * read arguments, if any, and make them macros in their own right
  495.      * arguments are read recursively using NextToken (right? I'm not
  496.      * sure...) 
  497.      */
  498.     arg = HEAD (&mac->arguments);
  499.     if (TEST (arg)) {
  500.  
  501.         /*
  502.          * get first open paren 
  503.          */
  504.         vtok = NextToken (&buf);
  505.         if (vtok != RT_CHR || *buf != '(')
  506.             goto argfail;
  507.  
  508.         /*
  509.          * loop through args separated with commas 
  510.          */
  511.         endarglist = 0;
  512.         while (TEST (arg)) {
  513.             smac = NEW (struct Macro);
  514.             if (!smac) goto argfail;
  515.             smac->name = arg->buf;
  516.             smac->active = 0;
  517.             smac->nargs = 0;
  518.             NewList (&smac->arguments);
  519.             NewList (&smac->tokens);
  520.  
  521.             /*
  522.              * scan out tokens allowing for nested commas 
  523.              */
  524.             level = 0;
  525.             while () {
  526.                 vtok = NextToken (&buf);
  527.                 if (vtok == RT_CHR) {
  528.                     if (*buf == '(' || *buf == '{')
  529.                         level++;
  530.                     if (*buf == '}')
  531.                         level--;
  532.                     if (*buf == ')') {
  533.                         if (level == 0) {
  534.                             endarglist = 1;
  535.                             break;
  536.                         }
  537.                         level--;
  538.                     }
  539.                     if (*buf == ',' && level == 0)
  540.                         break;
  541.                 }
  542.                 tok = CopyToken (lasttok);
  543.                 if (!tok) goto argfail;
  544.                 tok->can_be_macro = 0;
  545.                 AddTail (&smac->tokens, tok);
  546.             }
  547.             AddHead (¯os, smac);
  548.             if (endarglist) break;
  549.             arg = NEXT (arg);
  550.         }
  551.         if (vtok != RT_CHR || *buf != ')') goto argfail;
  552.     }
  553.  
  554.     /*
  555.      * Macro does not become active in the global list until all
  556.      * arguments have been parsed and interpreted. 
  557.      */
  558.     amac = NEW (struct ActiveMacro);
  559.     if (!amac) return;        /* what the fuck do I do here? */
  560.     amac->mac = mac;
  561.     amac->curtok = HEAD (&mac->tokens);
  562.     mac->active = 1;
  563.     AddHead (&activemacs, amac);
  564.     return;
  565.  
  566. argfail:
  567.     printf ("bad problem with arguments\n");
  568. }
  569.  
  570.  
  571. /*
  572.  * locate a specified macro in the available macros list 
  573.  */
  574. struct Macro * FindMacro (buf)
  575.     char           *buf;
  576. {
  577.     struct Macro   *mac;
  578.  
  579.     for (mac = HEAD (¯os); TEST (mac); mac = NEXT (mac))
  580.         if (mac->name == buf)
  581.             return mac;
  582.  
  583.     return NULL;
  584. }
  585.  
  586.  
  587. /*
  588.  * Do an "include" directive 
  589.  */
  590. Include ()
  591. {
  592.     short           pos, ok, global;
  593.     char            c;
  594.  
  595.     /*
  596.      * get filename 
  597.      */
  598.     RawToken ();
  599.     ok = 0;
  600.     global = 0;
  601.  
  602.     if (rawtok.type == RT_STR) {
  603.         ok = 1;
  604.         strcpy (basnam, rawtok.buf);
  605.     } else if (rawtok.type == RT_CHR && rawtok.chr == '<') {
  606.         ok = 1;
  607.         pos = 0;
  608.         while () {
  609.             c = NextC ();
  610.             if (c == '>') break;
  611.             basnam[pos++] = c;
  612.         }
  613.         basnam[pos] = 0;
  614.         global = 1;
  615.     }
  616.     AdvanceEOL ();
  617.     if (!ok) {
  618.         printf ("no file to include\n");
  619.         return;
  620.     }
  621.     if (!global) if (OpenLexFile (basnam)) return;
  622.     strcpy (libnam, libbase);
  623.     strcat (libnam, basnam);
  624.     if (OpenLexFile (libnam)) return;
  625.     printf ("error open include file <%s>\n", libnam);
  626. }
  627.  
  628.  
  629. /*
  630.  * Do the "define" directive.
  631.  */
  632. Define ()
  633. {
  634.     struct Macro   *mac = NULL;
  635.     struct Token   *tok;
  636.     struct StringElt *se;
  637.  
  638.     /*
  639.      * get identifier to define 
  640.      */
  641.     RawToken ();
  642.     if (rawtok.type != RT_ID) {
  643.         printf ("error in define - no identifier\n");
  644.         goto escape;
  645.     }
  646.     mac = NEW (struct Macro);
  647.     if (!mac) goto escape;
  648.     mac->name = rawtok.buf;
  649.     mac->active = 0;
  650.     mac->nargs = 0;
  651.     NewList (&mac->arguments);
  652.     NewList (&mac->tokens);
  653.  
  654.     /*
  655.      * Look for parenthized argument list.
  656.      */
  657.     if (toplf->nextchar == '(') {
  658.         RawToken ();
  659.         while () {
  660.             RawToken ();
  661.  
  662.             /*
  663.              * deal with special case of "#define MACRO()" 
  664.              */
  665.             if (!mac->nargs && rawtok.type == RT_CHR
  666.               && rawtok.chr == ')')
  667.                 break;
  668.             if (rawtok.type != RT_ID) {
  669.                 printf ("macro argument not an identifier\n");
  670.                 goto escape;
  671.             }
  672.             se = NEW (struct StringElt);
  673.             if (!se) goto escape;
  674.             se->buf = rawtok.buf;
  675.             AddTail (&mac->arguments, se);
  676.             mac->nargs++;
  677.             RawToken ();
  678.             if (rawtok.type != RT_CHR) {
  679.                 printf ("macro arg list delimiter not a character\n");
  680.                 goto escape;
  681.             }
  682.             if (rawtok.chr == ')') break;
  683.             if (rawtok.chr != ',') {
  684.                 printf ("macro arg list delimiter not ',' or ')'\n");
  685.                 goto escape;
  686.             }
  687.         }
  688.     }
  689.  
  690.     /*
  691.      * get the sequence of tokens which make up this definition 
  692.      */
  693.     while () {
  694.         RawToken ();
  695.         if (rawtok.type == RT_NEWLN || rawtok.type == RT_EOF) break;
  696.         tok = CopyToken (&rawtok);
  697.         if (!tok) goto escape;
  698.         tok->can_be_macro = 1;
  699.         AddTail (&mac->tokens, tok);
  700.     }
  701.  
  702.     AddHead (¯os, mac);
  703.     return;
  704.  
  705. escape:
  706.     if (mac) FreeMacro (mac);
  707.     AdvanceEOL ();
  708. }
  709.  
  710.  
  711. /*
  712.  * Skip over a defined-out section.  Returns 1 if it ends with #else, 0
  713.  * if it ends with #endif, -1 for EOF.  Keeps track of nested if's
  714.  * being skipped.
  715.  */
  716. short SkipRemovedSec ()
  717. {
  718.     short           level = 0;
  719.  
  720.     while () {
  721.         RawToken ();
  722.         if (rawtok.type == RT_EOF) return -1;
  723.         if (rawtok.type == RT_ESC) {
  724.             RawToken ();
  725.             if (rawtok.type == RT_ID) {
  726.                 if (rawtok.buf == ifdefstr
  727.                   || rawtok.buf == ifndefstr) level++;
  728.                 if (rawtok.buf == elsestr)
  729.                     if (!level) return 1;
  730.                 if (rawtok.buf == endifstr)
  731.                     if (!level--) return 0;
  732.             }
  733.         }
  734.     }
  735. }
  736.  
  737.  
  738. /*
  739.  * Does the "ifdef"/"ifndef" - "else" - "endif" directive.  Type is 1
  740.  * for ifdef and 0 for ifndef.
  741.  */
  742. IfDef (type)
  743.     short           type;
  744. {
  745.     struct Macro   *mac;
  746.     struct IfBlock *ib;
  747.     short           els;
  748.  
  749.     RawToken ();
  750.     mac = FindMacro (rawtok.buf);
  751.  
  752.     /*
  753.      * If condition is true, add a node to say "skip the next #else
  754.      * section" if false, skip this section.  If section ends with an
  755.      * else, add a junk node to pop off for consistency at next #endif.
  756.      */
  757.     if ((mac && type) || (!mac && !type)) {
  758.         ib = NEW (struct IfBlock);
  759.         if (!ib) return;
  760.         ib->skip_else = 1;
  761.         AddHead (&ifblocks, ib);
  762.     } else {
  763.         els = SkipRemovedSec ();
  764.         if (els == 1) {
  765.             ib = NEW (struct IfBlock);
  766.             if (!ib) return;
  767.             ib->skip_else = 0;
  768.             AddHead (&ifblocks, ib);
  769.         }
  770.     }
  771. }
  772.  
  773.  
  774. Else ()
  775. {
  776.     struct IfBlock *ib;
  777.     short           els;
  778.  
  779.     ib = HEAD (&ifblocks);
  780.     ASSERT (TEST (ib));
  781.     Remove (ib);
  782.     if (!ib->skip_else) {
  783.         printf ("strange \"else\" block found\n");
  784.     } else {
  785.         els = SkipRemovedSec ();
  786.         if (els == 1 && ib->skip_else) {
  787.             printf ("strange \"else\" block found\n");
  788.         }
  789.     }
  790.     FREI (ib);
  791. }
  792.  
  793.  
  794. EndIf ()
  795. {
  796.     struct IfBlock *ib;
  797.  
  798.     ib = HEAD (&ifblocks);
  799.     ASSERT (TEST (ib));
  800.     Remove (ib);
  801.     FREI (ib);
  802. }
  803.  
  804.  
  805. /*
  806.  * Backs up one token 
  807.  */
  808. Backspace ()
  809. {
  810.     retok = lasttok;
  811. }
  812.  
  813.  
  814. struct Token * CopyToken (tok)
  815.     struct Token   *tok;
  816. {
  817.     struct Token   *ntok;
  818.  
  819.     ntok = NEW (struct Token);
  820.     if (ntok) *ntok = *tok;
  821.     return ntok;
  822. }
  823.  
  824.  
  825.  
  826. /* HASH TABLE STUFF */
  827.  
  828.  
  829. InitHash ()
  830. {
  831.     short           i;
  832.  
  833.     for (i = 0; i < NHASH; i++)
  834.         NewList (&hashTab[i]);
  835. }
  836.  
  837.  
  838. /*
  839.  * Simple but (I hope) effective hash function.
  840.  */
  841. short HashVal (buf)
  842.     char           *buf;
  843. {
  844.     unsigned long   hash;
  845.  
  846.     hash = 0;
  847.     while (*buf) hash = hash * 3 + *buf++;
  848.     return hash % NHASH;
  849. }
  850.  
  851.  
  852. /*
  853.  * Finds (or creates) a stored string matching the given one return pointer
  854.  * which acts as unique ident for this string.
  855.  */
  856. char * FindString (buf)
  857.     char           *buf;
  858. {
  859.     short           hash;
  860.     struct Lst     *hlist;
  861.     struct StringElt *se;
  862.     char           *sto;
  863.  
  864.     hash = HashVal (buf);
  865.     hlist = &hashTab[hash];
  866.  
  867.     for (se = HEAD (hlist); TEST (se); se = NEXT (se)) {
  868.         if (!strcmp (se->buf, buf))
  869.             return se->buf;
  870.     }
  871.     se = NEW (struct StringElt);
  872.     if (!se) return NULL;
  873.     sto = NEW_N (char, strlen (buf) + 1);
  874.     if (!sto) {
  875.         FREI (se);
  876.         return NULL;
  877.     }
  878.     AddTail (hlist, se);
  879.     strcpy (sto, buf);
  880.     se->buf = sto;
  881.     return sto;
  882. }
  883.  
  884.  
  885. FreeHash ()
  886. {
  887.     short           i;
  888.     struct StringElt *se, *nse;
  889.  
  890.     for (i = 0; i < NHASH; i++) {
  891.         for (se = HEAD (&hashTab[i]); nse = NEXT (se); se = nse) {
  892.             FREE_N (se->buf, char, strlen (se->buf) + 1);
  893.             FREI (se);
  894.         }
  895.     }
  896. }
  897.